/*
 * Copyright (c) 2019.  drakelee.base All rights reserved.
 */

package com.base.components.task.controller;

import com.base.components.common.service.task.SysTask;
import com.base.components.common.util.JsonUtils;
import com.base.components.task.service.SysTaskService;
import com.base.components.task.service.TaskBroadcastType;
import com.base.components.task.service.TaskManager;
import com.fasterxml.jackson.core.type.TypeReference;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * 服务器内部广播controller
 *
 * @author <a href="drakelee1221@gmail.com">LiGeng</a>
 * @version 1.0.0, 2017-08-03 15:08
 */
@RestController
public class TaskBroadcastController {
  private static final Logger logger = LoggerFactory.getLogger(TaskBroadcastController.class);

  /** 广播请求的header标识键，防止外部伪造请求 */
  public static final String BROADCAST_AUTH_HEADER_KEY = "BROADCAST-AUTH";

  /** 广播请求的header标识值 */
  public static final String BROADCAST_AUTH_HEADER_VALUE = "A48F59480B8247CCA299A9E8E25A559A";

  /** 广播请求类型，键 */
  public static final String BROADCAST_TYPE_KEY = "BROADCAST_TYPE_KEY";

  /** 广播请求涉及的事件taskIds，键 */
  public static final String TASK_FIRE_IDS_KEY = "TASK_FIRE_IDS_KEY";

  /** 广播请求涉及的事件taskIds间隔符 */
  public static final String TASK_FIRE_IDS_SEPARATOR_CHARS = ";";

  /** 广播请求返回值中，判断成功或失败，键 */
  public static final String RETURN_RESULT_KEY = "result";

  /** 广播请求返回值中，成功处理条数，键 */
  public static final String RETURN_SUCCESS_COUNT = "successCount";

  /** 广播请求返回值中，广播类型，键 */
  public static final String RETURN_TYPE_KEY = "type";

  /** 广播请求返回值中，结果消息，键 */
  public static final String RETURN_MESSAGE_KEY = "msg";

  /** 广播请求地址 */
  public static final String BROADCAST_URL = "/task/broadcast";

  @Autowired
  private SysTaskService sysTaskService;

  @Autowired
  private TaskManager taskManager;


  /** 处理结果 */
  public enum BroadcastReturn {
    /** 处理成功 */
    SUCCESS,
    /** 处理失败 */
    FAIL
  }

  @RequestMapping(path = BROADCAST_URL, method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE, headers = {
    BROADCAST_AUTH_HEADER_KEY + "=" + BROADCAST_AUTH_HEADER_VALUE})
  public Map<String, Object> broadcast(@RequestParam("BROADCAST_TYPE_KEY") String type,
                                       @RequestParam(value = "TASK_FIRE_IDS_KEY", defaultValue = "") String fireIds,
                                       @RequestParam Map<String, String> params) {
    TaskBroadcastType t = TaskBroadcastType.parse(type);
    Assert.notNull(t, "广播类型为空！");
    List<String> ids = Lists.newArrayList(StringUtils.split(fireIds, TASK_FIRE_IDS_SEPARATOR_CHARS));
    Map<String, Object> map = null;
    switch (t) {
      case ADD:
        map = add(ids);
        break;
      case PAUSE:
        map = pause(ids);
        break;
      case RESTART:
        map = reStart(ids);
        break;
      case REBUILD:
        map = rebuildAll();
        break;
      case UNLOCK:
        map = unlock(ids);
        break;
      case TRIGGER: map = trigger(fireIds, params.get("argumentsMap"));
      default:
    }

    if (null != map) {
      map.put(RETURN_TYPE_KEY, t.toString());
    } else {
      map = returnMap(BroadcastReturn.FAIL, "广播类型未匹配");
    }
    return map;
  }

  private Map<String, Object> add(List<String> fireIds) {
    if (fireIds.isEmpty()) {
      return returnMap(BroadcastReturn.FAIL, "taskIds is null");
    }
    List<SysTask> fireTasks = getList(fireIds);
    if (fireTasks.isEmpty()) {
      return returnMap(BroadcastReturn.FAIL, "tasks not found, taskIds = " + fireIds);
    }
    int count = 0;
    try {
      for (SysTask task : fireTasks) {
        if (taskManager.addJob(task)) {
          count++;
          logger.info("广播新增调度任务成功：" + task);
        }
      }
    } catch (Throwable t) {
      logger.error("广播新增调度任务异常：" + fireIds, t);
      throw new IllegalArgumentException("广播新增调度任务异常：" + fireIds, t);
    }
    return returnMap(BroadcastReturn.SUCCESS, count);
  }

  private Map<String, Object> pause(List<String> fireIds) {
    if (fireIds.isEmpty()) {
      return returnMap(BroadcastReturn.FAIL, "taskIds is null");
    }
    List<SysTask> fireTasks = getList(fireIds);
    if (fireTasks.isEmpty()) {
      return returnMap(BroadcastReturn.FAIL, "tasks not found, taskIds = " + fireIds);
    }
    try {
      for (SysTask task : fireTasks) {
        taskManager.removeJob(task.getId());
        logger.info("广播暂停调度任务成功：" + task);
      }
    } catch (Throwable t) {
      logger.error("广播暂停调度任务异常：" + fireIds, t);
      throw new IllegalArgumentException("广播暂停调度任务异常：" + fireIds, t);
    }
    return returnMap(BroadcastReturn.SUCCESS);
  }

  private Map<String, Object> reStart(List<String> fireIds) {
    if (fireIds.isEmpty()) {
      return returnMap(BroadcastReturn.FAIL, "taskIds is null");
    }
    List<SysTask> fireTasks = getList(fireIds);
    if (fireTasks.isEmpty()) {
      return returnMap(BroadcastReturn.FAIL, "tasks not found, taskIds = " + fireIds);
    }
    int count = 0;
    try {
      for (SysTask task : fireTasks) {
        taskManager.removeJob(task.getId());
        if (taskManager.addJob(task)) {
          count++;
          logger.info("广播重启调度任务成功：" + task);
        }
      }
    } catch (Throwable t) {
      logger.error("广播重启调度任务异常：" + fireIds, t);
      throw new IllegalArgumentException("广播重启调度任务异常：" + fireIds, t);
    }
    return returnMap(BroadcastReturn.SUCCESS, count);
  }

  private Map<String, Object> rebuildAll() {
    int count;
    try {
      count = taskManager.reBuildCurrentServer(taskManager.findEnableTasks());
      logger.info("广播重构所有调度任务成功：" + count);
    } catch (Throwable t) {
      logger.error("广播重构所有调度任务异常", t);
      throw new IllegalArgumentException("广播重构所有调度任务异常", t);
    }
    return returnMap(BroadcastReturn.SUCCESS, count);
  }

  private Map<String, Object> unlock(List<String> fireIds) {
    if (fireIds.isEmpty()) {
      return returnMap(BroadcastReturn.FAIL, "taskIds is null");
    }
    int count;
    try {
      count = sysTaskService.unlock(fireIds);
      logger.info("广播解锁调度任务成功：" + count);
    } catch (Throwable t) {
      logger.error("广播解锁调度任务异常：" + fireIds, t);
      throw new IllegalArgumentException("广播解锁调度任务异常：" + fireIds, t);
    }
    return returnMap(BroadcastReturn.SUCCESS, count);
  }


  private Map<String, Object> trigger(String fireId, String argumentsMap) {
    if (StringUtils.isBlank(fireId)) {
      return returnMap(BroadcastReturn.FAIL, "taskId is null");
    }
    Map<String, Object> args = Maps.newHashMap();
    try {
      args = JsonUtils.reader(argumentsMap, new TypeReference<HashMap<String, Object>>() {});
    } catch (IOException ignored) {
    }
    try {
      taskManager.triggerTask(fireId, args);
    } catch (Throwable t) {
      logger.error("广播立即触发调度任务异常：" + fireId, t);
      throw new IllegalArgumentException("广播立即触发调度任务异常：" + fireId, t);
    }
    return returnMap(BroadcastReturn.SUCCESS);
  }

  private Map<String, Object> returnMap(BroadcastReturn re) {
    return returnMap(re, null, null);
  }

  private Map<String, Object> returnMap(BroadcastReturn re, Integer successCount) {
    return returnMap(re, null, successCount);
  }

  private Map<String, Object> returnMap(BroadcastReturn re, String message) {
    return returnMap(re, message, null);
  }

  private Map<String, Object> returnMap(BroadcastReturn re, String message, Integer successCount) {
    Map<String, Object> map = Maps.newHashMap();
    map.put(RETURN_RESULT_KEY, re.toString());
    if (null != message) {
      map.put(RETURN_MESSAGE_KEY, message);
    }
    if (null != successCount) {
      map.put(RETURN_SUCCESS_COUNT, successCount);
    }
    return map;
  }

  private List<SysTask> getList(List<String> ids) {
    if (ids == null || ids.isEmpty()) {
      return Collections.emptyList();
    }
    return sysTaskService.findByIds(ids);
  }

}
